﻿#include "mydn.h"
#include "localstorage.h"
#include "storage.h"

#if defined(XP_UNIX)
#include <pthread.h>
#include <sys/types.h>
#include <dirent.h>
#else
#include <Windows.h>
#endif

CLocalStorage::CLocalStorage(NPP npp)
: TNPObject<CLocalStorage>(npp)
{
	m_methods["update"] = &CLocalStorage::Method_update;
	m_methods["setpath"] = &CLocalStorage::Method_setpath;
	m_methods["getlist"] = &CLocalStorage::Method_getlist;
        m_methods["getuuid"] = &CLocalStorage::Method_getuuid;
}

CLocalStorage::~CLocalStorage()
{

}

bool CLocalStorage::HasMethod(NPIdentifier name)
{
	char*		pname	= gpnpf->utf8fromidentifier(name);
	std::map<std::string, METHOD>::iterator		it;

	it = m_methods.find(pname);
	if(it == m_methods.end())
		return false;

	return true;
}

bool CLocalStorage::Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
	char*		pname	= gpnpf->utf8fromidentifier(name);
	std::map<std::string, METHOD>::iterator		it;

	it = m_methods.find(pname);
	if(it == m_methods.end())
		return false;

	return (this->*(it->second))(args, argCount, result);
}
/*
* 更新方法
*/
bool CLocalStorage::Method_update(const NPVariant *args, uint32_t argCount, NPVariant *result)
{
	if(1 != argCount || !NPVARIANT_IS_OBJECT(args[0]))
	{
		gpnpf->setexception(this, "Invalidate parameter");
		return true;
	}

	NPObject*               pfun        = NPVARIANT_TO_OBJECT(args[0]);
	LPTHREAD_PARAMETER      ptp         = new THREAD_PARAMETER;

	ptp->pthis = this;
	ptp->pobject = pfun;

	gpnpf->retainobject(ptp->pobject);

#if defined(XP_UNIX)
	pthread_t               tid;

	pthread_create(&tid, NULL, thread_update, ptp);
#else
	HANDLE					hThread;

	hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_update, ptp, NULL, NULL);
	CloseHandle(hThread);
#endif
	return true;
}
/*
* 设置ROOTPATH方法
*/
bool CLocalStorage::Method_setpath(const NPVariant *args, uint32_t argCount, NPVariant *result)
{
	if(1 > argCount || !NPVARIANT_IS_STRING(args[0]))
	{
		gpnpf->setexception(this, "Invalidate parameter");
		return true;
	}
	NPString        str         = NPVARIANT_TO_STRING(args[0]);

	m_rootpath.resize(str.UTF8Length + 1, 0);
	memcpy((void *)m_rootpath.c_str(), str.UTF8Characters, str.UTF8Length);
	return true;
}

/*
* 更新处理
*/
void CLocalStorage::DoUpdate(NPObject *pobject)
{
	char                        szDBPath[512]   = {0};
	const char*                 proot           = m_rootpath.c_str();

	mydn::pathCombine(szDBPath, sizeof(szDBPath), proot, "mydn.db", '/');
	unlink(szDBPath);
	// 枚举文件
	if(NULL == proot)
	{
		call_update(&m_nppt, pobject, -1l, TR("未指定环境变量(MYDN_ROOT)"));
	}
	else
	{
		CStorage                    storage(szDBPath);
		std::vector<std::string>    vctDirs;
		char                        szHtml[512]             = {0};

		mydn::pathCombine(szHtml, sizeof(szHtml), proot, "html", '/');
		DbgTrace(("[CLocalStorage::thread_update] szDBPath: %s\r\n", szDBPath));
		if(false == storage.Create())
		{
			call_update(&m_nppt, pobject, -1l, TR("创建数据库失败: %s"), szDBPath);
			return;
		}
		call_update(&m_nppt, pobject, 0l, TR("开始更新"));
		CLocalStorage::EnumHtmlFiles(vctDirs, szHtml);
		DbgTrace(("[CLocalStorage::thread_update] file htmls: %d\r\n", vctDirs.size()));
		for(size_t i = 0; i < vctDirs.size(); i++)
		{
			DbgTrace(("[CLocalStorage::thread_update] read html: %s\r\n", vctDirs[i].c_str()));
			call_update(&m_nppt, pobject, static_cast<long>(i * 100 / vctDirs.size())
				, TR("找到文件: %s"), vctDirs[i].c_str());
			// 读取文件
			std::string         strData;

			if(false == mydn::getHtmlData(strData, vctDirs[i].c_str()))
			{
				call_update(&m_nppt, pobject, -1l, TR("读取文件失败: %s"), vctDirs[i].c_str());
				return;
			}
			storage.AddHtml(A2U(vctDirs[i].c_str()), strData.c_str());
		}
		call_update(&m_nppt, pobject, 100l, TR("更新完成"));
	}
}

/*
* 枚举HTML文件
*/
void CLocalStorage::EnumHtmlFiles(std::vector<std::string>& vct, const char* pDir)
{
#if defined(XP_UNIX)
	// LINUX操作
	DIR*            dir     = opendir(pDir);
	struct dirent*  pdt;

	if(NULL == dir)
		return;
	while(NULL != (pdt = readdir(dir)))
	{
		if(0 == strcmp(pdt->d_name, ".")
			|| 0 == strcmp(pdt->d_name, ".."))
		{
			continue;
		}
		// 分析文件
		char     szFile[512]     = {0};

		mydn::pathCombine(szFile, sizeof(szFile), pDir, pdt->d_name, '/');
		if(DT_DIR == pdt->d_type)
		{
			EnumHtmlFiles(vct, szFile);
			continue;
		}
		if(DT_REG != pdt->d_type)
			continue;
		// 分析文件后缀
		if(false == mydn::checkPathExtensionIsHtml(szFile))
			continue;
		vct.push_back(szFile);
	}
	closedir(dir);
#else
	// Window 操作
	char				szFind[512]		= {0};
	HANDLE				hFind			= NULL;
	WIN32_FIND_DATAA	fd				= {0};

	mydn::pathCombine(szFind, sizeof(szFind), pDir, "*");
	hFind = FindFirstFileA(szFind, &fd);
	if(NULL == hFind || INVALID_HANDLE_VALUE == hFind)
		return;
	do
	{
		if(FILE_ATTRIBUTE_DIRECTORY == (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
			continue;

		char			szFile[512]			= {0};
		
		mydn::pathCombine(szFile, sizeof(szFile), pDir, fd.cFileName);
		if(false == mydn::checkPathExtensionIsHtml(szFile))
			continue;
		vct.push_back(szFile);
	} while(FindNextFileA(hFind, &fd));
	FindClose(hFind);
#endif
}

/*
* 更新线程
*/
void* CLocalStorage::thread_update(void * parameter)
{
	LPTHREAD_PARAMETER          ptp         = (LPTHREAD_PARAMETER)parameter;

	ptp->pthis->DoUpdate(ptp->pobject);
	// 回收操作
	gpnpf->releaseobject(ptp->pobject);
	delete ptp;
	return NULL;
}

/*
* 调用更新回调函数
*/
void CLocalStorage::call_update(NPP npp, NPObject* object, long pos, const char* pmsg, ...)
{
	LPCALLBACK_PARAMETER        pcp             = new CALLBACK_PARAMETER;
	char                        szMsg[1024]     = {0};
	va_list                     vaMsg;

	pcp->npp = npp;
	pcp->object = object;

	va_start(vaMsg, pmsg);
	vsnprintf(szMsg, sizeof(szMsg), pmsg, vaMsg);
	va_end(vaMsg);

	pcp->param[0] = pos;
	pcp->param[1] = szMsg;

	gpnpf->retainobject(pcp->object);
	gpnpf->pluginthreadasynccall(pcp->npp, callback_update, pcp);
}

/*
* 调用更新函数
*/
void CLocalStorage::callback_update(void * parameter)
{
	LPCALLBACK_PARAMETER        pcp         =  (LPCALLBACK_PARAMETER)parameter;
	NPVariant                   ret;

	gpnpf->invokeDefault(pcp->npp, pcp->object, (NPVariant *)pcp->param, 2, &ret);
	gpnpf->releaseobject(pcp->object);
	delete pcp;
}
/*
* 获取列表
*/
bool CLocalStorage::Method_getlist(const NPVariant *args, uint32_t argCount, NPVariant *result)
{
	char                        szDBPath[512]   = {0};
	const char*                 proot           = m_rootpath.c_str();

	mydn::pathCombine(szDBPath, sizeof(szDBPath), proot, "mydn.db", '/');

	CStorage                    storage(szDBPath);
	std::string                 str;
	CNPVariant                  var;

	if(false == storage.GetJoinList(str))
	{
		gpnpf->setexception(this, "get data filed.");
		return true;
	}
	var = str.c_str();
	*result = var.Detach();
	return true;
}
/*
 * 获取UUID
 */
std::string GetUUID();
bool CLocalStorage::Method_getuuid(const NPVariant *args, uint32_t argCount, NPVariant *result)
{
    std::string         struuid     = GetUUID();
    CNPVariant          uuid        = struuid.c_str();

    *result = uuid.Detach();
    return true;
}

